<?php

declare(strict_types=1);

namespace App\Order\Service\OrderRefund;

use App\Common\Constants\ErrorCode;
use App\Common\Constants\Stakeholder;
use App\Order\Event\CrdRefund;
use App\Order\Event\ShopSMSPush;
use App\Order\Model\OrderGoodsModel;
use App\Order\Model\OrderRefundModel;
use App\Order\Service\Order\OrderAdminService;
use App\Order\Service\Order\OrderBaseService;
use App\Order\Service\Order\OrderServiceInterface;
use App\Order\Service\OrderGoodsService;
use App\Order\Service\RefundGoodsService;
use App\Resource\Model\SystemModel;
use App\Resource\Service\TeamLeaderService;
use App\Third\Service\Kz\KzRefundService;
use App\Third\Service\Log\KzRefundLogService;
use App\Third\Service\Log\WxRefundLogService;
use App\Third\Service\Wx\WxRefundService;
use App\User\Service\MemberService;
use Hyperf\Contract\LengthAwarePaginatorInterface;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Model;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;


class OrderRefundService extends OrderBaseService implements OrderServiceInterface

{
    /**
     * @Inject()
     * @var MemberService
     */
    private $memberService;

    /**
     * @Inject()
     * @var RefundGoodsService
     */
    private $refundGoodsService;

    /**
     * @Inject()
     * @var KzRefundService
     */
    private $kzRefundService;

    /**
     * @Inject()
     * @var WxRefundService
     */
    private $wxRefundService;

    /**
     * @Inject()
     * @var KzRefundLogService
     */
    private $kzRefundLogService;

    /**
     * @Inject()
     * @var WxRefundLogService
     */
    private $wxRefundLogService;

    /**
     * @Inject
     * @var TeamLeaderService
     */
    private $teamLeaderService;

    /**
     * 售后列表
     * @param array $where
     * @param array|string[] $field
     * @param int $perPage
     * @return LengthAwarePaginatorInterface
     */
    public function getList(array $where = [], array $field = ['*'], int $perPage = 5)
    {
        $where = $this->conditionWhere($where);

        $refund = OrderRefundModel::query()
            ->from('store_order_refund as refund')
            ->leftJoin('store_shop as shop', 'refund.shop_id', '=', 'shop.shop_id')
            ->leftJoin('store_member as member', 'refund.mid', '=', 'member.mid')
            ->selectRaw(implode(',', $field))
            ->when($where['refund_no'] ?? 0, function ($query, $refund_no) {
                return $query->where('refund.refund_no', $refund_no);
            })
            ->when($where['order_no'] ?? 0, function ($query, $order_no) {
                return $query->where('refund.order_no', $order_no);
            })
            ->when($where['mid'] ?? 0, function ($query, $mid) {
                return $query->where('refund.mid', $mid);
            })
            ->when($where['create_at'] ?? 0, function ($query, $create_at) {
                return $query->whereBetween('refund.create_at', $create_at);
            })
            ->when($where['refund_at'] ?? 0, function ($query, $refund_at) {
                return $query->whereBetween('refund.refund_at', $refund_at);
            })
            ->when($where['shop_id'] ?? 0, function ($query, $shop_id) {
                return $query->where('refund.shop_id', $shop_id);
            })
            ->when(is_numeric($where['status']), function ($query) use ($where) {
                return $query->where('refund.status', $where['status']);
            })
            ->when(is_numeric($where['is_whole']), function ($query) use($where) {
                return $query->where('refund.is_whole', $where['is_whole']);
            })
            ->when(is_numeric($where['order_type']), function ($query) use ($where) {
                return $query->where('refund.order_type', $where['order_type']);
            })
            ->when(is_numeric($where['order_source']), function ($query) use ($where) {
                return $query->where('refund.order_source', $where['order_source']);
            })
            ->latest('create_at')
            ->paginate($perPage);

        return $refund;
    }

    /**
     * @param array $where
     * @return array
     * @author ran
     * @date 2021-03-26 13:57
     * mailbox 466180170@qq.com
     */
    public function conditionWhere(array $where)
    {
        $createAt = $where['create_at'] ?? '';
        $refundAt = $where['refund_at'] ?? '';
        if (!empty($where['phone'])) {
            $where['mid'] = $this->memberService->findValue(['phone' => $where['phone']], 'mid');
        }
        if (!empty($createAt) && strlen($createAt) > 23) {
            $where['create_at'] = [trim(substr($createAt, 0, 19)), trim(substr($createAt, -19))];
        }
        if (!empty($createAt) && strlen($createAt) < 24) {
            $where['create_at'] = [trim(substr($createAt, 0, 10)), trim(substr($createAt, -10))];
        }
        if (!empty($refundAt) && strlen($refundAt) > 23) {
            $where['refund_at'] = [trim(substr($refundAt, 0, 19)), trim(substr($refundAt, -19))];
        }
        if (!empty($refundAt) && strlen($refundAt) < 24) {
            $where['refund_at'] = [trim(substr($refundAt, 0, 10)), trim(substr($refundAt, -10))];
        }
        return $where;
    }

    /**
     * 用户退信息
     * @param array $where
     * @param array|string[] $field
     * @return array|Builder|Model|object
     */
    public function findFirst(array $where = [], array $field = ['*'])
    {
        return OrderRefundModel::query()->where($where)->select($field)->first();
    }

    /**
     * 退款商品信息
     * @param $where
     * @param $field
     * @return array
     */
    public function getRefundGoodsInfo(array $where = [], array $field = ['*'])
    {
        $refund = $this->findFirst($where, $field);   // store_order_refund   (order_source, refund_commission)
        $orderGoods = $this->container->get(OrderAdminService::class)->goodsResult(['order_no' => $refund->order_no], ['goods_id', 'deductMoney', 'dis_price', 'commission']);// store_order_goods   (commission)
        $goods = $refund->goods;
        $goodsArr = [];
        foreach ($goods as $k => $v) {
            $goodsArr[$k]['goods_id'] = $v->goods_id; // 编号
            $goodsArr[$k]['goods_title'] = $v->goods_title; // 商品名称
            $goodsArr[$k]['selling_price'] = $v->selling_price; //退款金额
            $goodsArr[$k]['goods_spec'] = $v->goods_spec; // 商品规格
            $goodsArr[$k]['freight'] = '-'; // 运费
            $goodsArr[$k]['refund_number'] = $v->refund_number; // 退款数量
            $goodsArr[$k]['refund_subtotal'] = $v->refund_price; // 退款小计
            $goodsArr[$k]['is_whole'] = $refund->is_whole;      //退款类别
            $goodsArr[$k]['status'] = $refund->status;      //退款状态
            foreach ($orderGoods as $kk => $vv) {
                if ($v['goods_id'] == $vv['goods_id']) {
                    $goodsArr[$k]['deductMoney'] = $vv['deductMoney'];
                    if ($vv['deductMoney'] != 0) {
                        $goodsArr[$k]['refund_subtotal'] = bcmul((string)$vv['goods_dis_price'], (string)$v['refund_number'], 2); // 退款小计
                    }
                    $goodsArr[$k]['commission'] = $vv['commission'];
                }
            }
        }
        return ['goods' => $goodsArr, 'total_amount' => $refund->refund_money, 'total_commission' => $refund->refund_commission];
    }

    /**
     * 更新用户申请退款数据
     * @param array $where
     * @param array $data
     * @return int
     */
    public function update(array $where = [], array $data = [])
    {
        return OrderRefundModel::query()
            ->where($where)
            ->update($data);
    }

    /**
     * 创建售后
     * @param array $data
     * @return Builder|Model
     */
    public function create(array $data){
        return OrderRefundModel::query()->create($data);
    }

    /**
     * 拼团售后订单详情
     * @param array $where
     * @param array $field
     * @return Builder|Model|object|null
     */
    public function getOrderRefundDetail(array $where, array $field)
    {
        return OrderRefundModel::query()
            ->with('goods:refund_no,goods_id,goods_logo,goods_title,goods_spec,selling_price,refund_number,refund_price')
            ->select($field)
            ->where($where)
            ->first();
    }


    /**
     * 用户提交退款===>拒绝退款
     * @param string $refund_no
     * @return array
     */
    public function refuse(string $refund_no)
    {
        try {

            $updateOrderRefundData = ['status' => Stakeholder::REFUND_REFUSE, 'handle_type' => Stakeholder::REFUND_PROCESSED, 'refund_at' => date('Y-m-d H:i:s', time())];
            $updateOrderData = ['status' => Stakeholder::ORDER_REFUND_REFUSE];
            // 还原 order.goods => refund_number ; refund.goods => refund_number/refund_price
            $refundGoods = ['refund_number' => 0, 'refund_price' => 0];
            $orderGoods = ['refund_number' => 0, 'refund_price' => 0];

            Db::transaction(function () use ($refund_no, $updateOrderRefundData, $updateOrderData, $refundGoods, $orderGoods) {

                $refund = $this->findFirst(['refund_no' => $refund_no], ['order_no']);
                $this->update(['refund_no' => $refund_no], $updateOrderRefundData);
                $this->container->get(OrderAdminService::class)->update(['order_no' => $refund->order_no], $updateOrderData);
                $this->refundGoodsService->update(['refund_no' => $refund_no], $refundGoods);
                $this->container->get(OrderGoodsService::class)->update(['order_no' => $refund->order_no], $orderGoods);
            });
        } catch (\Exception $e) {
            return ['code' => 0, 'errorCode' => ErrorCode::NOT_IN_FORCE];
        }
        return ['code' => 1, 'msg' => '拒绝成功'];
    }
    /**
     * 用户提交退款====>同意退款
     * @param string $refund_no
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function agree(string $refund_no)
    {
        // 全部/部分
        $refundInfo = $this->findFirst(['refund_no' => $refund_no], ['order_no', 'is_whole','refund_commission']);

        // 支付系统和客至码
        $info = $this->getOrderInfoAndCus((string)$refundInfo['order_no']);
        if ($info['code'] == 0) {
            return $info;
        }

        // 退款商品列表
        $refundGoodsField = ['order_no', 'shop_id', 'goods_id', 'goods_title', 'selling_price', 'refund_number', 'refund_price', 'status','kz_self_num'];
        $refundGoodsList = $this->refundGoodsService->getRefundGoodsList(['refund_no' => $refund_no], $refundGoodsField)->toArray();

        // 团长退佣金
        $leader_id = $info['orderInfo']['leader_id'];
        if ($leader_id && $refundInfo['refund_commission'] > 0) {
            $this->teamLeaderService->backCommission($refundInfo['order_no'], $leader_id, $refundInfo['refund_commission']);
        }
        $info['orderInfo']['refundCommission'] = $refundInfo['refund_commission'] ?? 0;

        switch ($info['orderInfo']['shop_member_system']) {
            case Stakeholder::PAY_SYSTEM_TYPE_KZ:
                return $this->kzRefundService->applyKzRefund($refundGoodsList, (int)$refundInfo['is_whole'], $info['kzCusCode'], $info['orderInfo']);
                break;
            case Stakeholder::PAY_SYSTEM_TYPE_WX:
                return $this->wxRefundService->applyWxRefund($refundGoodsList, (int)$refundInfo['is_whole'], $info['kzCusCode'], $info['orderInfo']);
                break;
        }
    }


    /**
     * 商家退款
     * @param string $order_no
     * @param int $isWhole
     * @param array $refundGoodsList
     * @return array|void
     */
    public function shopRefund(string $order_no, int $isWhole, ?array $refundGoodsList = null)
    {

        $info = $this->getOrderInfoAndCus($order_no);
        if ($info['code'] == 0) {
            return $info;
        }

        switch ($info['orderInfo']['shop_member_system']) {
            case Stakeholder::PAY_SYSTEM_TYPE_KZ:
                return $this->kzRefundService->shopKzRefund($refundGoodsList, $isWhole, $info['kzCusCode'], $info['orderInfo']);
                break;
            case Stakeholder::PAY_SYSTEM_TYPE_WX:
                return $this->wxRefundService->shopWxRefund($refundGoodsList, $isWhole, $info['kzCusCode'], $info['orderInfo']);
                break;
        }
    }

    /**
     * 获取订单信息,客至码
     * @param string $order_no
     * @return array
     */
    public function getOrderInfoAndCus(string $order_no="")
    {
        // 订单信息
        $orderField = ['mid','shop_id','order_no','freight_price','total_price','status','appointment_time','create_at',
            'shop_member_system','order_type','order_source','pay_at','platform_type','leader_id','appointment_date','activity_id','free_parent_order_no'];
        $orderInfo = $this->container->get(OrderAdminService::class)->findFirst(['order_no' => $order_no], $orderField)->toArray();

        // 客至会员码
        $kzCusCode = $this->memberService->findValue(['mid' => $orderInfo['mid']], 'kz_cus_code');

        // 支付系统
        if (empty($orderInfo) || !in_array($orderInfo['shop_member_system'], [Stakeholder::PAY_SYSTEM_TYPE_KZ, Stakeholder::PAY_SYSTEM_TYPE_WX])) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '未知的支付系统'];
        }

        $endTime = strtotime($orderInfo['appointment_date'] . ' +2 day');
        if (time() > $endTime) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '未在规定时间内退款'];
        }

        return ['code' => 1, 'orderInfo' => $orderInfo, 'kzCusCode' => $kzCusCode];
    }


    /**
     * 申请售后提交
     * @param object $order
     * @param array $param
     * @return array
     */
    public function applyRefund(object $order, array $param)
    {
        $initStatus = $order->status;
        if ($order->status == Stakeholder::ORDER_REFUNDED || !empty($order->refund_at)){
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '该订单已退款，如需再次申请请联系门店~'];
        }
        $reserveTime = strtotime($order->appointment_date . ' +2 day');
        if (time() > $reserveTime) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '您的订单已超出平台申请时效,请联系相关门店进行售后哦~'];
        }
        try {
            Db::beginTransaction();
            $ids = array_column($param['goods_list'] ?? [], 'id');
            $goodsObj = OrderGoodsModel::query()
                ->whereIn('id',$ids)
                ->selectRaw('id,goods_id,goods_title,goods_spec,goods_logo,group_id,selling_price,number,activity_id,activity_goods_id,commission,kz_self_num')
                ->get();
            $goodsArr = collect($goodsObj)->keyBy('id')->toArray();
            $everyGoodsRefundAmt = [];
            foreach ($param['goods_list'] as $k => $v){
                array_push($everyGoodsRefundAmt, bcmul((string)$v['number'], (string)$goodsArr[$v['id']]['selling_price'], 2));
            }
            $refund_commission = 0;
            $refund_no = "XPT" . (int)(microtime(true) * 1000) . mt_rand(100000, 999999);
            $refundInfo = [
                'refund_no' => $refund_no,
                'mid' => $order->mid,
                'shop_id' => $order->shop_id,
                'order_no' => $order->order_no,
                'order_type' => $order->order_type,
                'refund_money' => array_sum($everyGoodsRefundAmt),
                'deal_type' => $order->deal_type,
                'is_whole' => $param['is_whole'] ?? 0,
                'refund_memo' => $param['refund_memo'] ?? '',
                'img_url' => $param['img_url'] ?? '',
                'is_merchant' => 2,
                'order_refund_status' => $param['order_refund_status'],
                'before_status' => $order->status,
                'create_at' => date('Y-m-d H:i:s'),
                'order_source' => $order->order_source
            ];
            $refundGoods = [];
            foreach ($param['goods_list'] as $k => $v) {
                $refundGoods[] = [
                    'mid' => $order->mid,
                    'shop_id' => $order->shop_id,
                    'order_no' => $order->order_no,
                    'goods_id' => $goodsArr[$v['id']]['goods_id'],
                    'goods_title' => $goodsArr[$v['id']]['goods_title'],
                    'goods_spec' => $goodsArr[$v['id']]['goods_spec'],
                    'goods_logo' => $goodsArr[$v['id']]['goods_logo'],
                    'selling_price' => $goodsArr[$v['id']]['selling_price'],
                    'refund_number' => $v['number'],
                    'refund_price' => bcmul((string)$v['number'], (string)$goodsArr[$v['id']]['selling_price'], 2),
                    'refund_no' => $refund_no,
                    'group_id' => $goodsArr[$v['id']]['group_id'],
                    'kz_self_num' => $goodsArr[$v['id']]['kz_self_num']
                ];
                $refund_commission += $goodsArr[$v['id']]['commission'] * $v['number'];
                $this->orderGoodsService->update(['id' => $v['id']], ['refund_number' => $v['number']]);
            }
            $refundInfo['refund_commission'] = $refund_commission;
            $this->create($refundInfo);
            $this->refundGoodsService->saveAll($refundGoods);
            $order->status = Stakeholder::ORDER_PENDING;
            $order->save();
            Db::commit();
        }catch (\Exception $e){
            Db::rollBack();
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '提交失败'];
        }

        // 次日达订单未超过截单时间直接退款
        if ($order->order_source == 1){
            // 截单时间
            $crdCutOffTime = SystemModel::query()->where('name', 'cut_off_time')->value('value');
            if ( time() < strtotime(date('Y-m-d ',strtotime($order->pay_at)) . $crdCutOffTime . ':00')){
                $this->eventDispatcher->dispatch(new CrdRefund($refund_no));
                return ['code' => 1, 'data' => ['refund_no' => $refund_no], 'msg' => '提交成功'];
            }
        }
        $this->sendRefundMsg($order);
        if ($initStatus == Stakeholder::ORDER_RECEIVED) {
            $this->teamLeaderService->backCommission($order['order_no'], $order['leader_id'], $refundInfo['refund_commission']);
        }
        return ['code' => 1, 'data' => ['refund_no' => $refund_no], 'msg' => '提交成功'];
    }
    /**
     * 发送退款短信通知
     * @param object $order
     */
    public function sendRefundMsg(object $order){
        // 商家发短信
        if (env('APP_ENV') != 'dev'){
            $this->eventDispatcher->dispatch(new ShopSMSPush($order->shop_id, $order->order_no));
        }
    }
    /**
     * 退款表退款额(当天)
     * @param int $shop_id
     * @return string
     */
    public function refundAmountDay(int $shop_id)
    {
        return OrderRefundModel::query()->where(['shop_id'=>$shop_id],['status'=>2])
            ->whereDate('refund_at', date('Y-m-d', time()))
            ->sum('refund_money');

    }

    /**
     * 退款表退款额(当月)
     * @param int $shop_id
     * @return string
     */
    public static function refundAmountMonth(int $shop_id)
    {
        return OrderRefundModel::query()->where(['shop_id'=>$shop_id],['status'=>2])
            ->whereMonth('refund_at', date('m', time()))
            ->sum('refund_money');
    }

    /**
     * 退款信息
     * @param array $orderNoArr
     * @return array
     */
    public function refundInfo(array $orderNoArr)
    {
        // 退款信息
        // 客至
        $kzRefundArr = $this->kzRefundLogService->refundInfo($orderNoArr);
        // 吾享
        $wxRefundArr = $this->wxRefundLogService->refundInfo($orderNoArr);

        $kzRefund = array_column($kzRefundArr, 'refund_balance_money', 'order_no');
        $wxRefund = array_column($wxRefundArr, 'refund_wx_money', 'order_no');

        return ['kzRefund' => $kzRefund, 'wxRefund' => $wxRefund];
    }

    public function delete()
    {
        // TODO: Implement delete() method.
    }

    public function info()
    {
        // TODO: Implement info() method.
    }
}
